﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="HeliClass.cs" company="">
//   
// </copyright>
// <summary>
//   The heli class.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace RescueRaiders.Models
{
    using System;

    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;

    /// <summary>
    /// The heli class.
    /// </summary>
    internal sealed class HeliClass : BaseModel
    {
        /// <summary>
        /// The face right.
        /// </summary>
        private const float faceRight = 12;

        /// <summary>
        /// The left turn.
        /// </summary>
        private const float leftTurn = faceRight / 3;

        /// <summary>
        /// The right turn.
        /// </summary>
        private const float rightTurn = faceRight * 2 / 3;

        /// <summary>
        /// The turn rate.
        /// </summary>
        private const float turnRate = 0.5f;

        /// <summary>
        /// The x accelerate.
        /// </summary>
        private const float xAccelerate = 0.5f;

        /// <summary>
        /// The x settling.
        /// </summary>
        private const float xSettling = xAccelerate * 0.5f;

        /// <summary>
        /// The y accelerate.
        /// </summary>
        private const float yAccelerate = 0.3f;

        /// <summary>
        /// The y settling.
        /// </summary>
        private const float ySettling = yAccelerate * 0.25f;

        /// <summary>
        /// The maximal velocity.
        /// </summary>
        private static readonly Vector2 maximalVelocity = new Vector2(12.0f, 3.0f);

        /// <summary>
        /// The break velocity.
        /// </summary>
        private readonly float breakVelocity = maximalVelocity.X / 3f;

        /// <summary>
        /// true if helicopter is not in landed Position
        /// </summary>
        private bool canMoveToSides;

        /// <summary>
        /// The current frame.
        /// </summary>
        private int currentFrame;

        /// <summary>
        /// true if you started turning right false if you started turing left
        /// </summary>
        private bool inTurn;

        /// <summary>
        /// The last rotation.
        /// </summary>
        private float lastRotation;

        /// <summary>
        /// the last Velocity
        /// </summary>
        private Vector2 lastXVelocity;

        /// <summary>
        /// The turning.
        /// </summary>
        private float turning;

        /// <summary>
        /// The velocity.
        /// </summary>
        private Vector2 velocity;

        /// <summary>
        /// Initializes a new instance of the <see cref="HeliClass"/> class.
        /// </summary>
        /// <param name="texture">
        /// The texture.
        /// </param>
        /// <param name="position">
        /// The position.
        /// </param>
        /// <param name="side">
        /// The side.
        /// </param>
        public HeliClass(Texture2D texture, Vector2 position, Side side)
            : base(texture, position, side)
        {
            frameSize = new Point(137, 53);
            Texturedata = new Color[(frameSize.X * frameSize.Y)];
            Texture.GetData(0, SourceRectangle, Texturedata, 0, Texturedata.Length);

            // Borderfix = new Vector2((float)frameSize.X / 2, (float)frameSize.Y / 2);
            Center = new Vector2(frameSize.X / 2f, frameSize.Y / 2f);
            turning = faceRight;
        }

        /// <summary>
        /// Gets CameraFix.
        /// </summary>
        public float CameraFix { get; private set; }

        // public Vector2 Borderfix { get; private set; }

        /// <summary>
        /// Gets IsFlip.
        /// </summary>
        protected override SpriteEffects IsFlip
        {
            get
            {
                return turning < leftTurn ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
            }
        }

        /// <summary>
        /// Gets Rotation.
        /// </summary>
        protected override float Rotation
        {
            get
            {
                if (lastXVelocity.X > Math.Abs(velocity.X) && Math.Abs(velocity.X) < breakVelocity)
                {
                    if (turning == faceRight)
                    {
                        lastRotation -= lastRotation / 2 + MathHelper.ToRadians(5);
                    }
                    else
                    {
                        lastRotation += Math.Abs(lastRotation / 2 - MathHelper.ToRadians(5));
                    }
                }
                else
                {
                    lastRotation = (velocity.X / maximalVelocity.X) * (MathHelper.Pi / 6);
                }

                return lastRotation;
            }
        }

        /// <summary>
        /// Gets SourceRectangle.
        /// </summary>
        protected override Rectangle? SourceRectangle
        {
            get
            {
                return new Rectangle(currentFrame * frameSize.X, 0, frameSize.X, frameSize.Y);
            }
        }

        /// <summary>
        /// The update.
        /// </summary>
        /// <param name="gameTime">
        /// The game time.
        /// </param>
        public override void Update(GameTime gameTime)
        {
            var state = Keyboard.GetState();

            // this section controls which frame to draw
            if ((faceRight > turning && turning > rightTurn) || (turning > 1 && turning < leftTurn))
            {
                currentFrame = 4;
            }

            if (leftTurn < turning && turning < rightTurn)
            {
                currentFrame = 5;
            }

            if (currentFrame <= 3 && (turning == 1 || turning == faceRight))
            {
                ++currentFrame;
            }

            if (currentFrame >= 3 && (turning == 1 || turning == faceRight))
            {
                currentFrame = 0;
            }

            // movement behavior of the helicopter
            var acceleration = new Vector2();
            if (state.IsKeyDown(Keys.Right) && canMoveToSides)
            {
                // determine if the key will make you turn right or accelerate
                // if (((-xAccelerate) <= Velocity.X) && Velocity.X <= 0 && turning != faceRight)
                if (velocity.X == 0 && turning != faceRight)
                {
                    turning += turnRate;
                    inTurn = true;
                }
                else
                {
                    acceleration.X += xAccelerate;
                    if (state.IsKeyDown(Keys.Right) && velocity.X < 0)
                    {
                        acceleration.X = 0;
                    }
                }
            }

            if (state.IsKeyDown(Keys.Left) && canMoveToSides)
            {
                // if (0 <= Velocity.X && Velocity.X <= (xAccelerate) && turning != 1)
                if (velocity.X == 0 && turning != 1)
                {
                    turning -= turnRate;
                    inTurn = false;
                }
                else
                {
                    acceleration.X -= xAccelerate;
                    if (state.IsKeyDown(Keys.Left) && velocity.X > 0)
                    {
                        acceleration.X = 0;
                    }
                }
            }

            // ordering to continue turning if you started
            if (state.IsKeyUp(Keys.Right) && state.IsKeyUp(Keys.Left) && turning != 1 && turning != faceRight)
            {
                if (inTurn)
                {
                    turning += turnRate;
                }
                else
                {
                    turning -= turnRate;
                }
            }

            if (state.IsKeyDown(Keys.Up))
            {
                acceleration.Y -= yAccelerate;
            }

            if (state.IsKeyDown(Keys.Down))
            {
                acceleration.Y += yAccelerate;
            }

            // this part is in charge of de accelerating if you havent pressed a key(so the Acceleration value is zero)
            // because we want is to be slower than the Acceleration we use settling value in the opposite direction
            if (acceleration.X == 0 && lastXVelocity.X >= xSettling)
            {
                acceleration.X = -Math.Sign(velocity.X) * xSettling;
            }
            else if (acceleration.X == 0 && lastXVelocity.X < xSettling)
            {
                velocity.X = 0;
            }

            if (acceleration.Y == 0 && lastXVelocity.Y >= ySettling)
            {
                acceleration.Y = -Math.Sign(velocity.Y) * ySettling;
            }
            else if (acceleration.Y == 0 && lastXVelocity.Y < ySettling)
            {
                velocity.Y = 0;
            }

            lastXVelocity.X = Math.Abs(velocity.X);
            lastXVelocity.Y = Math.Abs(velocity.Y);
            velocity += acceleration;
            velocity.X = MathHelper.Clamp(velocity.X, -maximalVelocity.X, maximalVelocity.X);
            velocity.Y = MathHelper.Clamp(velocity.Y, -maximalVelocity.Y, maximalVelocity.Y);

            // determing whether the heli is facing left or right
            if (velocity.X < 0 || (acceleration.X == 0 && lastXVelocity.X < 0))
            {
                turning = 1;
            }
            else if (velocity.X > 0 || (acceleration.X == 0 && lastXVelocity.X > 0))
            {
                turning = faceRight;
            }

            Position += velocity;
            var p = Position;
            p.X = MathHelper.Clamp(p.X, Game1.Level.Left, Game1.Level.Right - frameSize.X);
            p.Y = MathHelper.Clamp(p.Y, Game1.Level.Top + (frameSize.Y / 2), Game1.Level.Bottom - frameSize.Y);
            canMoveToSides = p.Y < (Game1.Level.Height - (Texture.Height / 6) - frameSize.Y);
            Position = p;

            // cameraFix brings the currect Velocity value in comparism to the maximal
            CameraFix = velocity.X / maximalVelocity.X;
        }
    }
}